package com.hh.flow.service.impl;

import com.hh.flow.service.HhDefService;
import com.warm.flow.core.FlowFactory;
import com.warm.flow.core.constant.ExceptionCons;
import com.warm.flow.core.domain.entity.FlowDefinition;
import com.warm.flow.core.domain.entity.FlowNode;
import com.warm.flow.core.domain.entity.FlowSkip;
import com.warm.flow.core.enums.NodeType;
import com.warm.flow.core.enums.SkipType;
import com.warm.flow.core.service.DefService;
import com.warm.flow.core.service.NodeService;
import com.warm.flow.core.service.SkipService;
import com.warm.flow.core.utils.AssertUtil;
import com.warm.tools.utils.CollUtil;
import com.warm.tools.utils.ObjectUtil;
import com.warm.tools.utils.StreamUtils;
import com.warm.tools.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author minliuhua
 * @description: 流程定义serviceImpl
 * @date: 2023/5/29 13:09
 */
@Service
public class HhDefServiceImpl implements HhDefService {

    @Resource
    private DefService defService;

    @Resource
    private NodeService nodeService;

    @Resource
    private SkipService skipService;

    @Override
    public List<FlowNode> getNodeList(Long definitionId) {
        FlowNode flowNode = new FlowNode();
        flowNode.setDefinitionId(definitionId);
        List<FlowNode> flowNodes = nodeService.list(flowNode);
        StringBuffer skipDescribe;
        Map<String, String> nodeMap = flowNodes.stream().collect(Collectors.toMap(FlowNode::getNodeCode, FlowNode::getNodeName));
        for (FlowNode node : flowNodes) {
            skipDescribe = new StringBuffer();
            FlowSkip skip = new FlowSkip();
            skip.setNodeId(node.getId());
            List<FlowSkip> skips = skipService.list(skip);
            for (FlowSkip flowSkip : skips) {
                if (StringUtils.isNotEmpty(SkipType.getValueByKey(flowSkip.getSkipType()))) {
                    skipDescribe.append("【").append(SkipType.getValueByKey(flowSkip.getSkipType()))
                            .append("】");
                }
                if (StringUtils.isNotEmpty(flowSkip.getSkipCondition())) {
                    skipDescribe.append("满足【").append(flowSkip.getSkipCondition())
                            .append("】条件，");
                }
                skipDescribe.append("流转到").append("【")
                        .append(nodeMap.get(flowSkip.getNextNodeCode())).append("】;  ");
            }
            if (StringUtils.isEmpty(skipDescribe.toString())) {
                node.setSkipDescribe("无");
            } else {
                node.setSkipDescribe(skipDescribe.toString());
            }
        }
        return flowNodes;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveNode(List<FlowNode> flowNodeList, Long definitionId) {
        List<Integer> nodeTypes = flowNodeList.stream().map(FlowNode::getNodeType).collect(Collectors.toList());
        AssertUtil.isFalse(nodeTypes.contains(NodeType.START.getKey())
                && nodeTypes.contains(NodeType.END.getKey()), "必须有开始结点和结束结点");

        // 校验每一个流程的开始结点个数
        int startNum = 0;
        for (FlowNode flowNode : flowNodeList) {
            AssertUtil.isFalse(StringUtils.isAllNotEmpty(flowNode.getNodeCode(), flowNode.getNodeName()), "结点编码和结点名称不能为空");
            AssertUtil.isTrue(ObjectUtil.isNull(flowNode.getNodeType()), "结点类型不能为空");
            if (NodeType.isStart(flowNode.getNodeType())) {
                startNum++;
                AssertUtil.isTrue(startNum > 1, ExceptionCons.MUL_START_NODE);
            }
        }

        FlowNode nodeQuery = new FlowNode();
        nodeQuery.setDefinitionId(definitionId);
        // 获取原始结点
        List<FlowNode> flowNodesOrig = nodeService.list(nodeQuery);

        Set<Long> origNodeIds = flowNodesOrig.stream().map(FlowNode::getId).collect(Collectors.toSet());
        Set<Long> tarNodeIds = flowNodeList.stream().map(FlowNode::getId).collect(Collectors.toSet());

        // 新增结点
        List<FlowNode> nodesAdd = new ArrayList<>();
        // 更新结点
        List<FlowNode> nodesUpdate = new ArrayList<>();
        for (FlowNode flowNode : flowNodeList) {
            if (ObjectUtil.isNull(flowNode.getId())) {
                nodesAdd.add(flowNode);
            } else if (origNodeIds.contains(flowNode.getId())) {
                nodesUpdate.add(flowNode);
            }
        }

        // 删除结点
        List<Long> nodesDelete = new ArrayList<>();
        for (Long origNodeId : origNodeIds) {
            if (!tarNodeIds.contains(origNodeId)) {
                nodesDelete.add(origNodeId);
            }
        }

        //  执行新增
        FlowDefinition flowDefinition = defService.getById(definitionId);
        for (FlowNode flowNode : nodesAdd) {
            flowNode.setDefinitionId(definitionId);
            flowNode.setVersion(flowDefinition.getVersion());
        }

        if (CollUtil.isNotEmpty(nodesAdd)) {
            nodeService.saveBatch(nodesAdd);
        }

        // 执行更新
        if (CollUtil.isNotEmpty(nodesUpdate)) {
            nodeService.updateBatch(nodesUpdate);
        }

        // 执行删除
        if (CollUtil.isNotEmpty(nodesDelete)) {
            skipService.deleteByNodeIds(nodesDelete);
            nodeService.removeByIds(nodesDelete);
        }
    }

    /**
     * 保存流程跳转
     *
     * @param skipList
     * @param nodeId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSkip(List<FlowSkip> skipList, Long nodeId) {
        FlowNode node = nodeService.getById(nodeId);
        // 校验
        for (FlowSkip flowSkip : skipList) {
            // 网关节点可不需要跳转类型
            if (!NodeType.isGateWay(node.getNodeType())) {
                AssertUtil.isFalse(StringUtils.isNotEmpty(flowSkip.getSkipType()), "跳转类型不能为空");
            }
            AssertUtil.isFalse(StringUtils.isNotEmpty(flowSkip.getNextNodeCode()), "下一个流程结点编码为空");
        }

        if (!NodeType.isEnd(node.getNodeType())) {
            AssertUtil.isTrue(CollectionUtils.isEmpty(skipList), "开始和中间结点必须有跳转规则");
        }
        // 中间节点的集合， 跳转类型和目标节点不能重复
        Set<String> betweenSet = new HashSet<>();
        // 网关的集合 跳转条件和下目标节点不能重复
        Set<String> gateWaySet = new HashSet<>();
        List<FlowNode> nodeList = FlowFactory.nodeService()
                .list(new FlowNode().setDefinitionId(node.getDefinitionId()));
        Map<String, FlowNode> flowNodeMap = StreamUtils.toMap(nodeList, FlowNode::getNodeCode, flowNode -> flowNode);
        // 遍历结点下的跳转条件
        for (FlowSkip skip : skipList) {
            skip.setNowNodeType(node.getNodeType());
            skip.setNextNodeType(flowNodeMap.get(skip.getNextNodeCode()).getNodeType());
            if (NodeType.isGateWaySerial(node.getNodeType())) {
                String target = skip.getSkipCondition() + ":" + skip.getNextNodeCode();
                AssertUtil.isTrue(gateWaySet.contains(target), ExceptionCons.SAME_CONDITION_NODE);
                gateWaySet.add(target);
            } else if (NodeType.isGateWayParallel(node.getNodeType())) {
                String target = skip.getNextNodeCode();
                AssertUtil.isTrue(gateWaySet.contains(target), ExceptionCons.SAME_DEST_NODE);
                gateWaySet.add(target);
            } else {
                String value = skip.getSkipType() + ":" + skip.getNextNodeCode();
                AssertUtil.isTrue(betweenSet.contains(value), ExceptionCons.SAME_CONDITION_VALUE);
                betweenSet.add(value);
            }
        }

        // 校验跳转节点的合法性
        checkSkipNode(node, skipList);

        skipService.deleteByNodeId(nodeId);
        skipService.saveBatch(skipList);
    }

    /**
     * 校验跳转节点的合法性
     *
     * @param node
     * @param skipList
     */
    private void checkSkipNode(FlowNode node, List<FlowSkip> skipList) {
        List<FlowSkip> allSkips = FlowFactory.skipService()
                .list(new FlowSkip().setDefinitionId(node.getDefinitionId()));
        allSkips = StreamUtils.filter(allSkips, skip -> !node.getId().equals(skip.getNodeId()));
        allSkips.addAll(skipList);

        List<FlowSkip> gatewaySkips = new ArrayList<>();
        for (FlowSkip allSkip : allSkips) {
            if (NodeType.isGateWay(allSkip.getNowNodeType())) {
                gatewaySkips.add(allSkip);
            }
            // 中间节点不可驳回到网关节点
            AssertUtil.isTrue(!NodeType.isGateWay(allSkip.getNowNodeType())
                            && SkipType.isReject(allSkip.getSkipType())
                            && NodeType.isGateWay(allSkip.getNextNodeType())
                    , ExceptionCons.BETWEEN_REJECT_GATEWAY);
        }
        // 校验相同类型网关节点不可直连
        for (FlowSkip gatewaySkip1 : gatewaySkips) {
            for (FlowSkip gatewaySkip2 : gatewaySkips) {
                AssertUtil.isTrue(gatewaySkip1.getNextNodeCode().equals(gatewaySkip2.getNowNodeCode())
                                && gatewaySkip1.getNowNodeType().equals(gatewaySkip2.getNowNodeType())
                        , ExceptionCons.SAME_NODE_CONNECT);
            }
        }

        // 中间节点不可通过或者驳回到多个中间节点，必须先流转到网关节点
        int passNum = 0;
        int rejectNum = 0;
        for (FlowSkip value : skipList) {
            if (NodeType.isBetween(value.getNowNodeType()) && NodeType.isBetween(value.getNextNodeType())) {
                if (SkipType.isPass(value.getSkipType())) {
                    passNum++;
                } else {
                    rejectNum++;
                }
            }
        }
        AssertUtil.isTrue(passNum > 1 || rejectNum > 1, ExceptionCons.MUL_SKIP_BETWEEN);
    }

}
